1   /*
2    * Javassist, a Java-bytecode translator toolkit.
3    * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
4    *
5    * The contents of this file are subject to the Mozilla Public License Version
6    * 1.1 (the "License"); you may not use this file except in compliance with
7    * the License.  Alternatively, the contents of this file may be used under
8    * the terms of the GNU Lesser General Public License Version 2.1 or later,
9    * or the Apache License Version 2.0.
10   *
11   * Software distributed under the License is distributed on an "AS IS" basis,
12   * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13   * for the specific language governing rights and limitations under the
14   * License.
15   */
16  
17  package scouter.javassist.bytecode;
18  
19  import java.io.ByteArrayOutputStream;
20  import java.io.DataInputStream;
21  import java.io.IOException;
22  import java.util.HashMap;
23  import java.util.Map;
24  
25  import scouter.javassist.bytecode.AnnotationDefaultAttribute;
26  import scouter.javassist.bytecode.AttributeInfo;
27  import scouter.javassist.bytecode.ByteArray;
28  import scouter.javassist.bytecode.ConstPool;
29  import scouter.javassist.bytecode.Descriptor;
30  import scouter.javassist.bytecode.annotation.Annotation;
31  import scouter.javassist.bytecode.annotation.AnnotationMemberValue;
32  import scouter.javassist.bytecode.annotation.AnnotationsWriter;
33  import scouter.javassist.bytecode.annotation.ArrayMemberValue;
34  import scouter.javassist.bytecode.annotation.BooleanMemberValue;
35  import scouter.javassist.bytecode.annotation.ByteMemberValue;
36  import scouter.javassist.bytecode.annotation.CharMemberValue;
37  import scouter.javassist.bytecode.annotation.ClassMemberValue;
38  import scouter.javassist.bytecode.annotation.DoubleMemberValue;
39  import scouter.javassist.bytecode.annotation.EnumMemberValue;
40  import scouter.javassist.bytecode.annotation.FloatMemberValue;
41  import scouter.javassist.bytecode.annotation.IntegerMemberValue;
42  import scouter.javassist.bytecode.annotation.LongMemberValue;
43  import scouter.javassist.bytecode.annotation.MemberValue;
44  import scouter.javassist.bytecode.annotation.ShortMemberValue;
45  import scouter.javassist.bytecode.annotation.StringMemberValue;
46  
47  
48  /**
49   * A class representing
50   * <code>RuntimeVisibleAnnotations_attribute</code> and
51   * <code>RuntimeInvisibleAnnotations_attribute</code>.
52   *
53   * <p>To obtain an AnnotationAttribute object, invoke
54   * <code>getAttribute(AnnotationsAttribute.visibleTag)</code>
55   * in <code>ClassFile</code>, <code>MethodInfo</code>,
56   * or <code>FieldInfo</code>.  The obtained attribute is a
57   * runtime visible annotations attribute.
58   * If the parameter is
59   * <code>AnnotationAttribute.invisibleTag</code>, then the obtained
60   * attribute is a runtime invisible one.
61   *
62   * <p>For example,
63   *
64   * <pre>
65   * import javassist.bytecode.annotation.Annotation;
66   *    :
67   * CtMethod m = ... ;
68   * MethodInfo minfo = m.getMethodInfo();
69   * AnnotationsAttribute attr = (AnnotationsAttribute)
70   *         minfo.getAttribute(AnnotationsAttribute.invisibleTag);
71   * Annotation an = attr.getAnnotation("Author");
72   * String s = ((StringMemberValue)an.getMemberValue("name")).getValue();
73   * System.out.println("@Author(name=" + s + ")");
74   * </pre>
75   *
76   * <p>This code snippet retrieves an annotation of the type <code>Author</code>
77   * from the <code>MethodInfo</code> object specified by <code>minfo</code>.
78   * Then, it prints the value of <code>name</code> in <code>Author</code>.
79   *
80   * <p>If the annotation type <code>Author</code> is annotated by a meta annotation:
81   *
82   * <pre>
83   * &#64;Retention(RetentionPolicy.RUNTIME)
84   * </pre>
85   *
86   * <p>Then <code>Author</code> is visible at runtime.  Therefore, the third
87   * statement of the code snippet above must be changed into:
88   *
89   * <pre>
90   * AnnotationsAttribute attr = (AnnotationsAttribute)
91   *         minfo.getAttribute(AnnotationsAttribute.visibleTag);
92   * </pre>
93   *
94   * <p>The attribute tag must be <code>visibleTag</code> instead of
95   * <code>invisibleTag</code>.
96   *
97   * <p>If the member value of an annotation is not specified, the default value
98   * is used as that member value.  If so, <code>getMemberValue()</code> in
99   * <code>Annotation</code> returns <code>null</code>
100  * since the default value is not included in the
101  * <code>AnnotationsAttribute</code>.  It is included in the
102  * <code>AnnotationDefaultAttribute</code> of the method declared in the
103  * annotation type.
104  *
105  * <p>If you want to record a new AnnotationAttribute object, execute the
106  * following snippet:
107  *
108  * <pre>
109  * ClassFile cf = ... ;
110  * ConstPool cp = cf.getConstPool();
111  * AnnotationsAttribute attr
112  *     = new AnnotationsAttribute(cp, AnnotationsAttribute.visibleTag);
113  * Annotation a = new Annotation("Author", cp);
114  * a.addMemberValue("name", new StringMemberValue("Chiba", cp));
115  * attr.setAnnotation(a);
116  * cf.addAttribute(attr);
117  * cf.setVersionToJava5();
118  * </pre>
119  *
120  * <p>The last statement is necessary if the class file was produced by
121  * <code>javac</code> of JDK 1.4 or earlier.  Otherwise, it is not necessary.
122  *
123  * @see AnnotationDefaultAttribute
124  * @see scouter.javassist.bytecode.annotation.Annotation
125  */
126 public class AnnotationsAttribute extends AttributeInfo {
127     /**
128      * The name of the <code>RuntimeVisibleAnnotations</code> attribute.
129      */
130     public static final String visibleTag = "RuntimeVisibleAnnotations";
131 
132     /**
133      * The name of the <code>RuntimeInvisibleAnnotations</code> attribute.
134      */
135     public static final String invisibleTag = "RuntimeInvisibleAnnotations";
136 
137     /**
138      * Constructs a <code>Runtime(In)VisibleAnnotations_attribute</code>.
139      *
140      * @param cp            constant pool
141      * @param attrname      attribute name (<code>visibleTag</code> or
142      *                      <code>invisibleTag</code>).
143      * @param info          the contents of this attribute.  It does not
144      *                      include <code>attribute_name_index</code> or
145      *                      <code>attribute_length</code>.
146      */
147     public AnnotationsAttribute(ConstPool cp, String attrname, byte[] info) {
148         super(cp, attrname, info);
149     }
150 
151     /**
152      * Constructs an empty
153      * <code>Runtime(In)VisibleAnnotations_attribute</code>.
154      * A new annotation can be later added to the created attribute
155      * by <code>setAnnotations()</code>.
156      *
157      * @param cp            constant pool
158      * @param attrname      attribute name (<code>visibleTag</code> or
159      *                      <code>invisibleTag</code>).
160      * @see #setAnnotations(Annotation[])
161      */
162     public AnnotationsAttribute(ConstPool cp, String attrname) {
163         this(cp, attrname, new byte[] { 0, 0 });
164     }
165 
166     /**
167      * @param n     the attribute name.
168      */
169     AnnotationsAttribute(ConstPool cp, int n, DataInputStream in)
170         throws IOException
171     {
172         super(cp, n, in);
173     }
174 
175     /**
176      * Returns <code>num_annotations</code>.
177      */
178     public int numAnnotations() {
179         return ByteArray.readU16bit(info, 0);
180     }
181 
182     /**
183      * Copies this attribute and returns a new copy.
184      */
185     public AttributeInfo copy(ConstPool newCp, Map classnames) {
186         Copier copier = new Copier(info, constPool, newCp, classnames);
187         try {
188             copier.annotationArray();
189             return new AnnotationsAttribute(newCp, getName(), copier.close());
190         }
191         catch (Exception e) {
192             throw new RuntimeException(e);
193         }
194     }
195 
196     /**
197      * Parses the annotations and returns a data structure representing
198      * the annotation with the specified type.  See also
199      * <code>getAnnotations()</code> as to the returned data structure.
200      *
201      * @param type      the annotation type.
202      * @return null if the specified annotation type is not included.
203      * @see #getAnnotations()
204      */
205     public Annotation getAnnotation(String type) {
206         Annotation[] annotations = getAnnotations();
207         for (int i = 0; i < annotations.length; i++) {
208             if (annotations[i].getTypeName().equals(type))
209                 return annotations[i];
210         }
211 
212         return null;
213     }
214 
215     /**
216      * Adds an annotation.  If there is an annotation with the same type,
217      * it is removed before the new annotation is added.
218      *
219      * @param annotation        the added annotation.
220      */
221     public void addAnnotation(Annotation annotation) {
222         String type = annotation.getTypeName();
223         Annotation[] annotations = getAnnotations();
224         for (int i = 0; i < annotations.length; i++) {
225             if (annotations[i].getTypeName().equals(type)) {
226                 annotations[i] = annotation;
227                 setAnnotations(annotations);
228                 return;
229             }
230         }
231 
232         Annotation[] newlist = new Annotation[annotations.length + 1];
233         System.arraycopy(annotations, 0, newlist, 0, annotations.length);
234         newlist[annotations.length] = annotation;
235         setAnnotations(newlist);
236     }
237 
238     /**
239      * Parses the annotations and returns a data structure representing
240      * that parsed annotations.  Note that changes of the node values of the
241      * returned tree are not reflected on the annotations represented by
242      * this object unless the tree is copied back to this object by
243      * <code>setAnnotations()</code>.
244      *
245      * @see #setAnnotations(Annotation[])
246      */
247     public Annotation[] getAnnotations() {
248         try {
249             return new Parser(info, constPool).parseAnnotations();
250         }
251         catch (Exception e) {
252             throw new RuntimeException(e);
253         }
254     }
255 
256     /**
257      * Changes the annotations represented by this object according to
258      * the given array of <code>Annotation</code> objects.
259      *
260      * @param annotations           the data structure representing the
261      *                              new annotations.
262      */
263     public void setAnnotations(Annotation[] annotations) {
264         ByteArrayOutputStream output = new ByteArrayOutputStream();
265         AnnotationsWriter writer = new AnnotationsWriter(output, constPool);
266         try {
267             int n = annotations.length;
268             writer.numAnnotations(n);
269             for (int i = 0; i < n; ++i)
270                 annotations[i].write(writer);
271 
272             writer.close();
273         }
274         catch (IOException e) {
275             throw new RuntimeException(e);      // should never reach here.
276         }
277 
278         set(output.toByteArray());
279     }
280 
281     /**
282      * Changes the annotations.  A call to this method is equivalent to:
283      * <pre>setAnnotations(new Annotation[] { annotation })</pre>
284      *
285      * @param annotation    the data structure representing
286      *                      the new annotation.
287      */
288     public void setAnnotation(Annotation annotation) {
289         setAnnotations(new Annotation[] { annotation });
290     }
291 
292     /**
293      * @param oldname       a JVM class name.
294      * @param newname       a JVM class name.
295      */
296     void renameClass(String oldname, String newname) {
297         HashMap map = new HashMap();
298         map.put(oldname, newname);
299         renameClass(map);
300     }
301 
302     void renameClass(Map classnames) {
303         Renamer renamer = new Renamer(info, getConstPool(), classnames);
304         try {
305             renamer.annotationArray();
306         } catch (Exception e) {
307             throw new RuntimeException(e);
308         }
309     }
310 
311     void getRefClasses(Map classnames) { renameClass(classnames); }
312 
313     /**
314      * Returns a string representation of this object.
315      */
316     public String toString() {
317         Annotation[] a = getAnnotations();
318         StringBuilder sbuf = new StringBuilder();
319         int i = 0;
320         while (i < a.length) {
321             sbuf.append(a[i++].toString());
322             if (i != a.length)
323                 sbuf.append(", ");
324         }
325 
326         return sbuf.toString();
327     }
328 
329     static class Walker {
330         byte[] info;
331 
332         Walker(byte[] attrInfo) {
333             info = attrInfo;
334         }
335 
336         final void parameters() throws Exception {
337             int numParam = info[0] & 0xff;
338             parameters(numParam, 1);
339         }
340 
341         void parameters(int numParam, int pos) throws Exception {
342             for (int i = 0; i < numParam; ++i)
343                 pos = annotationArray(pos);
344         }
345 
346         final void annotationArray() throws Exception {
347             annotationArray(0);
348         }
349 
350         final int annotationArray(int pos) throws Exception {
351             int num = ByteArray.readU16bit(info, pos);
352             return annotationArray(pos + 2, num);
353         }
354 
355         int annotationArray(int pos, int num) throws Exception {
356             for (int i = 0; i < num; ++i)
357                 pos = annotation(pos);
358 
359             return pos;
360         }
361 
362         final int annotation(int pos) throws Exception {
363             int type = ByteArray.readU16bit(info, pos);
364             int numPairs = ByteArray.readU16bit(info, pos + 2);
365             return annotation(pos + 4, type, numPairs);
366         }
367 
368         int annotation(int pos, int type, int numPairs) throws Exception {
369             for (int j = 0; j < numPairs; ++j)
370                 pos = memberValuePair(pos);
371 
372             return pos;
373         }
374 
375         /**
376          * {@code element_value_paris}
377          */
378         final int memberValuePair(int pos) throws Exception {
379             int nameIndex = ByteArray.readU16bit(info, pos);
380             return memberValuePair(pos + 2, nameIndex);
381         }
382 
383         /**
384          * {@code element_value_paris[]}
385          */
386         int memberValuePair(int pos, int nameIndex) throws Exception {
387             return memberValue(pos);
388         }
389 
390         /**
391          * {@code element_value}
392          */
393         final int memberValue(int pos) throws Exception {
394             int tag = info[pos] & 0xff;
395             if (tag == 'e') {
396                 int typeNameIndex = ByteArray.readU16bit(info, pos + 1);
397                 int constNameIndex = ByteArray.readU16bit(info, pos + 3);
398                 enumMemberValue(pos, typeNameIndex, constNameIndex);
399                 return pos + 5;
400             }
401             else if (tag == 'c') {
402                 int index = ByteArray.readU16bit(info, pos + 1);
403                 classMemberValue(pos, index);
404                 return pos + 3;
405             }
406             else if (tag == '@')
407                 return annotationMemberValue(pos + 1);
408             else if (tag == '[') {
409                 int num = ByteArray.readU16bit(info, pos + 1);
410                 return arrayMemberValue(pos + 3, num);
411             }
412             else { // primitive types or String.
413                 int index = ByteArray.readU16bit(info, pos + 1);
414                 constValueMember(tag, index);
415                 return pos + 3;
416             }
417         }
418 
419         /**
420          * {@code const_value_index}
421          */
422         void constValueMember(int tag, int index) throws Exception {}
423 
424         /**
425          * {@code enum_const_value}
426          */
427         void enumMemberValue(int pos, int typeNameIndex, int constNameIndex)
428             throws Exception {
429         }
430 
431         /**
432          * {@code class_info_index}
433          */
434         void classMemberValue(int pos, int index) throws Exception {}
435 
436         /**
437          * {@code annotation_value}
438          */
439         int annotationMemberValue(int pos) throws Exception {
440             return annotation(pos);
441         }
442 
443         /**
444          * {@code array_value}
445          */
446         int arrayMemberValue(int pos, int num) throws Exception {
447             for (int i = 0; i < num; ++i) {
448                 pos = memberValue(pos);
449             }
450 
451             return pos;
452         }
453     }
454 
455     static class Renamer extends Walker {
456         ConstPool cpool;
457         Map classnames;
458 
459         /**
460          * Constructs a renamer.  It renames some class names
461          * into the new names specified by <code>map</code>.
462          *
463          * @param info      the annotations attribute.
464          * @param cp        the constant pool.
465          * @param map       pairs of replaced and substituted class names.
466          *                  It can be null.
467          */
468         Renamer(byte[] info, ConstPool cp, Map map) {
469             super(info);
470             cpool = cp;
471             classnames = map;
472         }
473 
474         int annotation(int pos, int type, int numPairs) throws Exception {
475             renameType(pos - 4, type);
476             return super.annotation(pos, type, numPairs);
477         }
478 
479         void enumMemberValue(int pos, int typeNameIndex, int constNameIndex)
480             throws Exception
481         {
482             renameType(pos + 1, typeNameIndex);
483             super.enumMemberValue(pos, typeNameIndex, constNameIndex);
484         }
485 
486         void classMemberValue(int pos, int index) throws Exception {
487             renameType(pos + 1, index);
488             super.classMemberValue(pos, index);
489         }
490 
491         private void renameType(int pos, int index) {
492             String name = cpool.getUtf8Info(index);
493             String newName = Descriptor.rename(name, classnames);
494             if (!name.equals(newName)) {
495                 int index2 = cpool.addUtf8Info(newName);
496                 ByteArray.write16bit(index2, info, pos);
497             }
498         }
499     }
500 
501     static class Copier extends Walker {
502         ByteArrayOutputStream output;
503         AnnotationsWriter writer;
504         ConstPool srcPool, destPool;
505         Map classnames;
506 
507         /**
508          * Constructs a copier.  This copier renames some class names
509          * into the new names specified by <code>map</code> when it copies
510          * an annotation attribute.
511          *
512          * @param info      the source attribute.
513          * @param src       the constant pool of the source class.
514          * @param dest      the constant pool of the destination class.
515          * @param map       pairs of replaced and substituted class names.
516          *                  It can be null.
517          */
518         Copier(byte[] info, ConstPool src, ConstPool dest, Map map) {
519             this(info, src, dest, map, true); 
520         }
521 
522         Copier(byte[] info, ConstPool src, ConstPool dest, Map map, boolean makeWriter) {
523             super(info);
524             output = new ByteArrayOutputStream();
525             if (makeWriter)
526                 writer = new AnnotationsWriter(output, dest);
527 
528             srcPool = src;
529             destPool = dest;
530             classnames = map;
531         }
532 
533         byte[] close() throws IOException {
534             writer.close();
535             return output.toByteArray();
536         }
537 
538         void parameters(int numParam, int pos) throws Exception {
539             writer.numParameters(numParam);
540             super.parameters(numParam, pos);
541         }
542 
543         int annotationArray(int pos, int num) throws Exception {
544             writer.numAnnotations(num);
545             return super.annotationArray(pos, num);
546         }
547 
548         int annotation(int pos, int type, int numPairs) throws Exception {
549             writer.annotation(copyType(type), numPairs);
550             return super.annotation(pos, type, numPairs);
551         }
552 
553         int memberValuePair(int pos, int nameIndex) throws Exception {
554             writer.memberValuePair(copy(nameIndex));
555             return super.memberValuePair(pos, nameIndex);
556         }
557 
558         void constValueMember(int tag, int index) throws Exception {
559             writer.constValueIndex(tag, copy(index));
560             super.constValueMember(tag, index);
561         }
562 
563         void enumMemberValue(int pos, int typeNameIndex, int constNameIndex)
564             throws Exception
565         {
566             writer.enumConstValue(copyType(typeNameIndex), copy(constNameIndex));
567             super.enumMemberValue(pos, typeNameIndex, constNameIndex);
568         }
569 
570         void classMemberValue(int pos, int index) throws Exception {
571             writer.classInfoIndex(copyType(index));
572             super.classMemberValue(pos, index);
573         }
574 
575         int annotationMemberValue(int pos) throws Exception {
576             writer.annotationValue();
577             return super.annotationMemberValue(pos);
578         }
579 
580         int arrayMemberValue(int pos, int num) throws Exception {
581             writer.arrayValue(num);
582             return super.arrayMemberValue(pos, num);
583         }
584 
585         /**
586          * Copies a constant pool entry into the destination constant pool
587          * and returns the index of the copied entry.
588          *
589          * @param srcIndex      the index of the copied entry into the source
590          *                      constant pool.
591          * @return the index of the copied item into the destination
592          *         constant pool.
593          */
594         int copy(int srcIndex) {
595             return srcPool.copy(srcIndex, destPool, classnames);
596         }
597 
598         /**
599          * Copies a constant pool entry into the destination constant pool
600          * and returns the index of the copied entry.  That entry must be
601          * a Utf8Info representing a class name in the L<class name>; form.
602          *
603          * @param srcIndex  the index of the copied entry into the source
604          *                  constant pool.
605          * @return          the index of the copied item into the destination
606          *                  constant pool.
607          */
608         int copyType(int srcIndex) {
609             String name = srcPool.getUtf8Info(srcIndex);
610             String newName = Descriptor.rename(name, classnames);
611             return destPool.addUtf8Info(newName);
612         }
613     }
614 
615     static class Parser extends Walker {
616         ConstPool pool;
617         Annotation[][] allParams;   // all parameters
618         Annotation[] allAnno;       // all annotations
619         Annotation currentAnno;     // current annotation
620         MemberValue currentMember;  // current member
621 
622         /**
623          * Constructs a parser.  This parser constructs a parse tree of
624          * the annotations.
625          *
626          * @param info      the attribute.
627          * @param src       the constant pool.
628          */
629         Parser(byte[] info, ConstPool cp) {
630             super(info);
631             pool = cp;
632         }
633 
634         Annotation[][] parseParameters() throws Exception {
635             parameters();
636             return allParams;
637         }
638 
639         Annotation[] parseAnnotations() throws Exception {
640             annotationArray();
641             return allAnno;
642         }
643 
644         MemberValue parseMemberValue() throws Exception {
645             memberValue(0);
646             return currentMember;
647         }
648 
649         void parameters(int numParam, int pos) throws Exception {
650             Annotation[][] params = new Annotation[numParam][];
651             for (int i = 0; i < numParam; ++i) {
652                 pos = annotationArray(pos);
653                 params[i] = allAnno;
654             }
655 
656             allParams = params;
657         }
658 
659         int annotationArray(int pos, int num) throws Exception {
660             Annotation[] array = new Annotation[num];
661             for (int i = 0; i < num; ++i) {
662                 pos = annotation(pos);
663                 array[i] = currentAnno;
664             }
665 
666             allAnno = array;
667             return pos;
668         }
669 
670         int annotation(int pos, int type, int numPairs) throws Exception {
671             currentAnno = new Annotation(type, pool);
672             return super.annotation(pos, type, numPairs);
673         }
674 
675         int memberValuePair(int pos, int nameIndex) throws Exception {
676             pos = super.memberValuePair(pos, nameIndex);
677             currentAnno.addMemberValue(nameIndex, currentMember);
678             return pos;
679         }
680 
681         void constValueMember(int tag, int index) throws Exception {
682             MemberValue m;
683             ConstPool cp = pool;
684             switch (tag) {
685             case 'B' :
686                 m = new ByteMemberValue(index, cp);
687                 break;
688             case 'C' :
689                 m = new CharMemberValue(index, cp);
690                 break;
691             case 'D' :
692                 m = new DoubleMemberValue(index, cp);
693                 break;
694             case 'F' :
695                 m = new FloatMemberValue(index, cp);
696                 break;
697             case 'I' :
698                 m = new IntegerMemberValue(index, cp);
699                 break;
700             case 'J' :
701                 m = new LongMemberValue(index, cp);
702                 break;
703             case 'S' :
704                 m = new ShortMemberValue(index, cp);
705                 break;
706             case 'Z' :
707                 m = new BooleanMemberValue(index, cp);
708                 break;
709             case 's' :
710                 m = new StringMemberValue(index, cp);
711                 break;
712             default :
713                 throw new RuntimeException("unknown tag:" + tag);
714             }
715 
716             currentMember = m;
717             super.constValueMember(tag, index);
718         }
719 
720         void enumMemberValue(int pos, int typeNameIndex, int constNameIndex)
721             throws Exception
722         {
723             currentMember = new EnumMemberValue(typeNameIndex,
724                                               constNameIndex, pool);
725             super.enumMemberValue(pos, typeNameIndex, constNameIndex);
726         }
727 
728         void classMemberValue(int pos, int index) throws Exception {
729             currentMember = new ClassMemberValue(index, pool);
730             super.classMemberValue(pos, index);
731         }
732 
733         int annotationMemberValue(int pos) throws Exception {
734             Annotation anno = currentAnno;
735             pos = super.annotationMemberValue(pos);
736             currentMember = new AnnotationMemberValue(currentAnno, pool);
737             currentAnno = anno;
738             return pos;
739         }
740 
741         int arrayMemberValue(int pos, int num) throws Exception {
742             ArrayMemberValue amv = new ArrayMemberValue(pool);
743             MemberValue[] elements = new MemberValue[num];
744             for (int i = 0; i < num; ++i) {
745                 pos = memberValue(pos);
746                 elements[i] = currentMember;
747             }
748 
749             amv.setValue(elements);
750             currentMember = amv;
751             return pos;
752         }
753     }
754 }